package com.xxxy.util;


import com.xxxy.redis.service.RedisService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class JwtUtils {

    @Autowired
    private RedisService redisService;

    //用户账号
    private static final String CLAIM_USER_ID = "user_account";

    //用户类型
    private static final String CLAIM_USER_TYPE = "user_type";

    //创建时间
    private static final String CLAIM_KEY_CREATED = "created";

    //密钥
    private String secret = "one piece";

    //过期时间
    private Long expiration = 60 * 60 * 24 * 14L;

    /**
     * 生成token
     */
    private String generateToken(Map<String, Object> claims) {
        return Jwts.builder().setClaims(claims)
                .setExpiration(generateExpirationDate())
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }

    /**
     * 获取token的负载(token解密)
     */
    private Claims getClaimsFromToken(String token) throws Exception {
        Claims claims = null;
        claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
        return claims;
    }

    /**
     * 生成token的过期时间
     */
    private Date generateExpirationDate() {

        return new Date(System.currentTimeMillis() + 1000 * expiration);
    }

    /**
     * 从token中获取登录方式
     */
    public String getUserIdFromToken(String token) throws Exception {
        String userId = getClaimsFromToken(token).get(CLAIM_USER_ID, String.class);
//        log.info("userId = {}", userId);
        return userId;
    }

    /**
     * 从token中获取登录方式
     */
    public Integer getUserTypeFromToken(String token) throws Exception {
        Integer userType = getClaimsFromToken(token).get(CLAIM_USER_TYPE, Integer.class);
        return userType;
    }

    /**
     * 判断token是否已经过期
     */
    public boolean isTokenExpired(String token) throws Exception {
        Date expiredDate = getExpiredDateFromToken(token);
        return expiredDate.before(new Date());
    }

    /**
     * 获取token过期时间
     */
    private Date getExpiredDateFromToken(String token) throws Exception {
        Claims claims = getClaimsFromToken(token);
        return claims.getExpiration();
    }

    /**
     * 根据管理员信息生成token
     */
    public String generateToken(String content, Integer type) {
        Map<String, Object> claims = new HashMap<>();
        //存放登录方式和创建时间
        claims.put(CLAIM_USER_ID, content + "");
        claims.put(CLAIM_USER_TYPE, type);
        claims.put(CLAIM_KEY_CREATED, new Date());
        return generateToken(claims);
    }

    /**
     * 刷新token
     */
    public String refreshToken(String token) throws Exception {
        if (StringUtils.isEmpty(token)) {
            return null;
        }
        //token校验不通过
        Claims claims = getClaimsFromToken(token);
        if (claims == null) {
            return null;
        }
        //如果token已经过期，不支持刷新
        if (isTokenExpired(token)) {
            return null;
        }
        //如果token在30分钟之内刚刷新过，返回原token
        if (!tokenRefreshJustBefore(token, 30 * 60)) {
            log.info("刷新了token");
            claims.put(CLAIM_KEY_CREATED, new Date());
            token = generateToken(claims);
            //更新Redis过期时间
            String userId = getUserIdFromToken(token);
            redisService.expire(userId, expiration);
        }
        return token;
    }

    /**
     * 判断token在指定时间内是否刚刚刷新过
     * time单位为秒
     */
    private boolean tokenRefreshJustBefore(String token, int time) throws Exception {
        Claims claims = getClaimsFromToken(token);
        Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
        Date refreshDate = new Date();
        //刷新时间在创建时间的指定时间内
        if (refreshDate.after(created) && (created.getTime() + time * 1000) > refreshDate.getTime()) {
            return true;
        }
        return false;
    }

    /**
     * 获取过期时间
     */
    public Long getExpiration() {
        return expiration;
    }
}
